#include "certmanager.h"

#ifndef ENABLE_GMSSL
#include <openssl/x509.h>
#include <openssl/x509_vfy.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#endif

#include <QtDebug>

#include "constants.h"



#define CHECK(x, z) \
  if(!x) {          \
    fprintf(stderr, "%s,%d: %s", __FUNCTION__, __LINE__, z); \
    assert(0);      \
  }

CertManager::CertManager(QObject *parent)
  : QObject{parent},
    CertTableModel::Delegate()
{
}

CertManager::~CertManager()
{

}

void CertManager::InitKeyUsageMap()
{
  key_usage_name_ = { ("Digital Signalture"),  // 0
                      ( "Non Repudiation"),  // 1
                      ( "Key Encipherment"),  // 2
                      ( "Data Encipherment"),  // 3
                      ( "Key Agreement"),  // 4
                      ( "Key Cert Sign"),  // 5
                      ( "CRL Sign"),  // 6
                      ( "Encipher Only"),  // 7
                      ( "Decipher Only"),  // 8
  };
  // key_usage_name_.append("Digital Signalture");  // 0
}

void CertManager::Init(bool with_nss)
{
#ifndef ENABLE_GMSSL
  InitKeyUsageMap();


  if(with_nss)
    assert(0);

  SSL_library_init();

  x509_store_ = X509_STORE_new();
  if(!X509_STORE_set_default_paths(x509_store_)) {
    int err = ERR_get_error();
    ReportError(1, "certificate store load path fail: %s, %s",
                CERT_PATH_POSIX,
                ERR_lib_error_string(err));
    assert(0);
  }

  // if(!X509_STORE_load_locations(x509_store_, NULL, CERT_PATH_POSIX)) {
  //   int err = ERR_get_error();
  //   ReportError(1, "certificate store load path fail: %s, %s",
  //               CERT_PATH_POSIX,
  //               ERR_lib_error_string(err));
  //   assert(0);
  // }


  // X509_LOOKUP* lookup = X509_STORE_add_lookup(x509_store_, X509_LOOKUP_store());
  // X509_LOOKUP* lookup = X509_STORE_add_lookup(x509_store_, X509_LOOKUP_meth_new("system"));

  // X509_LOOKUP* lookup = X509_LOOKUP_new(X509_LOOKUP_new("system"));



  // // // X509_LOOKUP_load_file_ex(lookup, CERT_PATH_POSIX, X509_FILETYPE_DEFAULT, NULL, NULL);
  // // // X509_LOOKUP_load_file_ex(lookup, CERT_PATH_POSIX, X509_FILETYPE_PEM, NULL, NULL);
  // // X509_LOOKUP_add_dir(lookup, CERT_PATH_POSIX, X509_FILETYPE_DEFAULT);
  // // X509_LOOKUP_add_dir(lookup, CERT_PATH_POSIX, X509_FILETYPE_PEM);

  // // X509_LOOKUP_add_store(lookup, CERT_PATH_POSIX);
  // X509_LOOKUP_add_dir(lookup, CERT_PATH_POSIX, X509_FILETYPE_PEM);

  if(!X509_STORE_load_locations(x509_store_, "/etc/ssl/certs/ca-certificates.crt", CERT_PATH_POSIX)) {
    int err = ERR_get_error();
    ReportError(1, "certificate store load path fail: %s, %s",
                CERT_PATH_POSIX,
                ERR_lib_error_string(err));
    assert(0);
  }


  x509_store_ctx_ = X509_STORE_CTX_new();
  assert(x509_store_ctx_);

  if(X509_STORE_CTX_init(x509_store_ctx_, x509_store_, NULL, NULL) != 1) {
    int err = ERR_get_error();
    ReportError(1, "X509_STORE_CTX_init fail: %s",
                ERR_lib_error_string(err));
    assert(0);
  }

  // int num_certs = X509_LOOKUP_by_subject(lookup, X509_LU_X509, NULL, NULL);
  // qInfo() << "number of trust anchors: " << num_certs;
  qInfo() << "X509_get_default_cert_dir_env: " <<X509_get_default_cert_dir_env();
  qInfo() << "ossl_safe_getenv(SSL_CERT_DIR): " << secure_getenv(X509_get_default_cert_dir_env());

  certs_ = X509_STORE_get0_objects(x509_store_);
  if(!certs_) {
    int err = ERR_get_error();
    ReportError(1, "X509_STORE_get0_objects fail: %s",
                ERR_lib_error_string(err));
    assert(0);
  }

  TraverseCert();
#endif
}

void CertManager::UnInit()
{
#ifndef ENABLE_GMSSL
  X509_STORE_CTX_free(x509_store_ctx_);
  X509_STORE_free(x509_store_);
#endif
}

bool CertManager::FindCert(const QString &keyword)
{
    
}

bool CertManager::VerifyCert(const QString &path, QString &errorMessage)
{

}

bool CertManager::ExportCert(const QString &path, int type, QString &errorMessage)
{
    
}

void CertManager::SetOption()
{
    
}

void CertManager::ReportError(int error_code, const QString &format, ...)
{
  va_list args;
  va_start(args, format);
  QString message;
  message.vsprintf(format.toUtf8(), args);
  va_end(args);
  qWarning() << "Error code: " << error_code << "; message: " << message;
}

void CertManager::TraverseCert()
{
#ifndef ENABLE_GMSSL
  int num_certs = sk_X509_OBJECT_num(certs_);
  for(int i = 0; i < num_certs; i++) {
    X509_OBJECT* obj = sk_X509_OBJECT_value(certs_, i);
    if(X509_OBJECT_get_type(obj) == X509_LU_X509) {
      X509* cert = X509_OBJECT_get0_X509(obj);
      ReadCert(cert);


    }
  }
#endif
}

void CertManager::ReadCertFile(const char *filePath)
{






}

QList<X509 *> CertManager::GetCertificates(const QString &category)
{

  if(sort_type_ == SortType::ByLogicalPosition)
    return position_map_.values(category);
  else if(sort_type_ == SortType::ByKeyUsage)
    return key_usage_map_.values(category);
}

QStringList CertManager::GetCategories()
{
  QStringList categories;
  if(sort_type_ == SortType::ByLogicalPosition)
    categories = position_map_.keys();
  else if(sort_type_ == SortType::ByKeyUsage)
    categories = key_usage_map_.keys();
  categories.sort();
  categories.removeDuplicates();
  return categories;
}

void CertManager::ReadCert(X509 *cert)
{
#ifndef ENABLE_GMSSL
  char subject[X509_NAME_LEN], issuer[X509_NAME_LEN];
  CHECK(!!cert, "cert should not be NULL");
  X509_NAME_oneline(X509_get_subject_name(cert), subject, X509_NAME_LEN);
  X509_NAME_oneline(X509_get_issuer_name(cert), issuer, X509_NAME_LEN);
  fprintf(stderr, "CERT ISSUER: %s, subject: %s\n", issuer, subject);


  ASN1_BIT_STRING* usage = (ASN1_BIT_STRING*)X509_get_ext_d2i(
        cert, NID_key_usage, NULL, NULL);
  if(!usage) {
    key_usage_map_.insert("Undefined", cert);
  }

  for(int i = 0; i < key_usage_name_.size(); i++) {
    if(ASN1_BIT_STRING_get_bit(usage, i))
      key_usage_map_.insert(key_usage_name_[i], cert);
  }
  QStringList extended_key_usage = std::move(GetExtendedKeyUsage(cert));
  if(!extended_key_usage.empty()) {
    for(const auto& usage: extended_key_usage)
      key_usage_map_.insert(usage, cert);
  }
#endif
}

QStringList CertManager::GetExtendedKeyUsage(X509 *cert)
{
#ifndef ENABLE_GMSSL
  QStringList result;
  STACK_OF(ASN1_OBJECT)* eku =
      (decltype(eku))X509_get_ext_d2i(cert, NID_ext_key_usage, NULL, NULL);
  if(!eku) {
    ReportError(1, "NO EKU FOUND OR ERROR RETRIVING EKU");
    return {};
  }

  int num_eku = sk_ASN1_OBJECT_num(eku);
  char obj_buf[80];
  for(int i = 0; i < num_eku; i++) {
    ASN1_OBJECT* obj = sk_ASN1_OBJECT_value(eku, i);
    memset(obj_buf, 0, sizeof(obj_buf));
    OBJ_obj2txt(obj_buf, sizeof(obj_buf), obj, 0);
    result.append(obj_buf);
  }
  sk_ASN1_OBJECT_pop_free(eku, ASN1_OBJECT_free);
  return std::move(result);
#else
  return QStringList();
#endif
}

